#include "qe_log.h"
#include "qe_string.h"
#include "qe_driver.h"
#include "libfdt.h"



QELOG_DOMAIN(QELOG_DOMAIN_DM);



QE_DRIVER_START(start);
QE_DRIVER_END(end);



static qe_driver *driver_tab_base = QE_NULL;
static qe_size driver_tab_size = 0;



static int driver_matched(qe_driver *driver, const char *compatible, int len) 
{
    const qe_device_id *match;

    for (match = driver->of_match; match->compatible; match++) {
        if (qe_strcmp(compatible, match->compatible) == 0) {
            return 1;
        }
    }

    return 0;
}

static void fdt_match(const void *fdt, int offset)
{
    int i;
    int len;
    const char *name;
    const char *compatible;
    qe_driver *driver;

    name = fdt_get_name(fdt, offset, &len);

    compatible = fdt_getprop(fdt, offset, "compatible", &len);
    if (!compatible || len <= 0) {
        return;
    }

    qe_debug("%s %s", name, compatible);

    for (i=0; i<driver_tab_size; i++) {

        driver = &driver_tab_base[i];

        if (!driver->of_match)
            continue;

        if (driver_matched(driver, compatible, len)) {
            qe_debug("match dev:%s drv:%s", name, driver->name);
            if (driver->probe)
                driver->probe(fdt, offset);
        }
    }
}

static void fdt_scan(const void *fdt, int start)
{
    int ret;
    int offset;

    for (offset = fdt_first_subnode(fdt, start); 
         offset >= 0;
         offset = fdt_next_subnode(fdt, offset)) {
        
        fdt_match(fdt, offset);

        fdt_scan(fdt, offset);
    }
}

static void dm_scan_fdt(const void *fdt)
{
    fdt_scan(fdt, 0);
}

void qe_dm_scan(const void *fdt)
{
    #if defined(__CC_ARM) || (defined(__ARMCC_VERSION) && __ARMCC_VERSION >= 6000000)
        driver_tab_base = (qe_driver *)&qe_driver_list$$base;
        driver_tab_size = ((qe_size)(&qe_driver_list$$limit)
                                - (qe_size)(&qe_driver_list$$base))
                                / sizeof(qe_driver);
    #elif defined(__ICCARM__) || defined(__ICCRX__)
        driver_tab_base = (qe_driver *)(__section_begin("qe_driver_list"));
        driver_tab_size = ((qe_size)(__section_end("qe_driver_list"))
                                - (qe_size)(__section_begin("qe_driver_list")))
                                / sizeof(qe_driver);
    #elif defined(__GNUC__)
#ifdef _WIN32
        qe_driver *base = (qe_driver *)(&qe_driver_list_start);
        driver_tab_base = &base[1];
        driver_tab_size = ((qe_size)(&qe_driver_list_end)
                                - (qe_size)(&qe_driver_list_start))
                                / sizeof(qe_driver) - 2;
#else
        driver_tab_base = (qe_driver *)(&qe_driver_list_start);
        driver_tab_size = ((qe_size)(&qe_driver_list_end)
                                - (qe_size)(&qe_driver_list_start))
                                / sizeof(qe_driver);

#endif
    #else
        #error not supported compiler, please use earlycon table mode
    #endif

    qe_debug("start scan %p %d", driver_tab_base, driver_tab_size);

    dm_scan_fdt(fdt);
}

void qe_driver_force_include(void)
{

#if (CONFIG_AXI_INTC)
    QE_DRIVER_FORCE_IMPORT(axi_intc);
#endif

#if (CONFIG_AXI_GPIO)
    QE_DRIVER_FORCE_IMPORT(axi_gpio);
#endif

#if (CONFIG_PS7_GPIO)
    QE_DRIVER_FORCE_IMPORT(ps7_gpio);
#endif

#if (CONFIG_AXI_HWICAP)
    QE_DRIVER_FORCE_IMPORT(axi_hwicap);
#endif

#if (CONFIG_PS7_XDCFG)
    QE_DRIVER_FORCE_IMPORT(ps7_xdcfg);
#endif

#if (CONFIG_AXI_I2C)
    QE_DRIVER_FORCE_IMPORT(axi_i2c);
#endif

#if (CONFIG_AXI_I2C_V2)
    QE_DRIVER_FORCE_IMPORT(axi_i2c_v2);
#endif

#if (CONFIG_AXI_UART16550)
    QE_DRIVER_FORCE_IMPORT(axi_uart16550);
#endif

#if (CONFIG_AXI_UART16550_EARLYCON)
    QE_EARLYCON_FORCE_IMPORT(axi_uart16550);
#endif

#if (CONFIG_AXI_UARTLITE)
    QE_DRIVER_FORCE_IMPORT(axi_uartlite);
#endif

#if (CONFIG_AXI_UART16550_EARLYCON)
    QE_EARLYCON_FORCE_IMPORT(axi_uartlite);
#endif

#if (CONFIG_PS7_UART)
    QE_DRIVER_FORCE_IMPORT(ps7_uart);
#endif

#if (CONFIG_PS7_UART_EARLYCON)
    QE_EARLYCON_FORCE_IMPORT(ps7_uart);
#endif

#if (CONFIG_PSU_UART)
    QE_DRIVER_FORCE_IMPORT(psu_uart);
#endif

#if (CONFIG_PSU_UART_EARLYCON)
    QE_EARLYCON_FORCE_IMPORT(psu_uart);
#endif

#if (CONFIG_XLNX_MDM_EARLYCON)
    QE_EARLYCON_FORCE_IMPORT(xlnx_mdm);
#endif

#if (CONFIG_AXI_QSPI)
    QE_DRIVER_FORCE_IMPORT(axi_qspi);
#endif

#if (CONFIG_PS7_QSPI)
    QE_DRIVER_FORCE_IMPORT(ps7_qspi);
#endif

#if (CONFIG_PS7_SPI)
    QE_DRIVER_FORCE_IMPORT(ps7_spi);
#endif

#if (CONFIG_PSU_QSPI)
    QE_DRIVER_FORCE_IMPORT(psu_qspi);
#endif

#if (CONFIG_AXI_TIMER)
    QE_DRIVER_FORCE_IMPORT(axi_timer);
#endif
}